//Copyright (c) 2012-2013 The cwkshell Authors. All rights reserved. 
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be 
// found in the LICENSE file. 
#ifndef CUC_CWKSHELL_VIEW_SHELL_UNLOAD_CONTROLLER_H_ 
#define  CUC_CWKSHELL_VIEW_SHELL_UNLOAD_CONTROLLER_H_ 

#include <set>

#include "base/memory/weak_ptr.h"
//#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"



namespace content {
	class NotificationSource;
	class NotifictaionDetails;
	class WebContents;
}

namespace cuc{ 
	namespace shell{ 

		class TabStripModel;
		class TabStripSelectionModel;

		class ShellWindow;

		////////////////////////////////////////////////////////////////////////////////
		//
		// TabStripModelObserver
		//
		//  Objects implement this interface when they wish to be notified of changes
		//  to the TabStripModel.
		//
		//  Two major implementers are the TabStrip, which uses notifications sent
		//  via this interface to update the presentation of the strip, and the Browser
		//  object, which updates bookkeeping and shows/hides individual content::WebContentses.
		//
		//  Register your TabStripModelObserver with the TabStripModel using its
		//  Add/RemoveObserver methods.
		//
		////////////////////////////////////////////////////////////////////////////////
		class TabStripModelObserver {
		public:
			// Enumeration of the possible values supplied to TabChangedAt.
			enum TabChangeType {
				// Only the loading state changed.
				LOADING_ONLY,

				// Only the title changed and page isn't loading.
				TITLE_NOT_LOADING,

				// Change not characterized by LOADING_ONLY or TITLE_NOT_LOADING.
				ALL
			};

			// A new content::WebContents was inserted into the TabStripModel at the
			// specified index. |foreground| is whether or not it was opened in the
			// foreground (selected).
			virtual void TabInsertedAt(content::WebContents* contents,
				int index,
				bool foreground);

			// The specified content::WebContents at |index| is being closed (and eventually
			// destroyed). |tab_strip_model| is the TabStripModel the tab was part of.
			virtual void TabClosingAt(TabStripModel* tab_strip_model,
				content::WebContents* contents,
				int index);

			// The specified content::WebContents at |index| is being detached, perhaps to
			// be inserted in another TabStripModel. The implementer should take whatever
			// action is necessary to deal with the content::WebContents no longer being
			// present.
			virtual void TabDetachedAt(content::WebContents* contents, int index);

			// The active content::WebContents is about to change from |old_contents|.
			// This gives observers a chance to prepare for an impending switch before it
			// happens.
			virtual void TabDeactivated(content::WebContents* contents);

			// Sent when the active tab changes. The previously active tab is identified
			// by |old_contents| and the newly active tab by |new_contents|. |index| is
			// the index of |new_contents|. |user_gesture| specifies whether or not this
			// was done by a user input event (e.g. clicking on a tab, keystroke) or as a
			// side-effect of some other function.
			// Note: It is possible for the selection to change while the active tab
			// remains unchanged. For example, control-click may not change the active tab
			// but does change the selection. In this case |ActiveTabChanged| is not sent.
			// If you care about any changes to the selection, override
			// TabSelectionChanged.
			virtual void ActiveTabChanged(content::WebContents* old_contents,
				content::WebContents* new_contents,
				int index,
				bool user_gesture);

			// Sent when the selection changes in |tab_strip_model|. More precisely when
			// selected tabs, anchor tab or active tab change. |old_model| is a snapshot
			// of the selection model before the change. See also ActiveTabChanged for
			// details.
			virtual void TabSelectionChanged(TabStripModel* tab_strip_model,
				const TabStripSelectionModel& old_model);

			// The specified content::WebContents at |from_index| was moved to |to_index|.
			virtual void TabMoved(content::WebContents* contents,
				int from_index,
				int to_index);

			// The specified content::WebContents at |index| changed in some way. |contents|
			// may be an entirely different object and the old value is no longer
			// available by the time this message is delivered.
			//
			// See TabChangeType for a description of |change_type|.
			virtual void TabChangedAt(content::WebContents* contents,
				int index,
				TabChangeType change_type);

			// The tab contents was replaced at the specified index. This is invoked
			// when instant is enabled and the user navigates by way of instant or when
			// prerendering swaps in a prerendered content::WebContents.
			virtual void TabReplacedAt(TabStripModel* tab_strip_model,
				content::WebContents* old_contents,
				content::WebContents* new_contents,
				int index);

			// Invoked when the pinned state of a tab changes. See note in
			// TabMiniStateChanged as to how this relates to TabMiniStateChanged.
			virtual void TabPinnedStateChanged(content::WebContents* contents, int index);

			// Invoked if the mini state of a tab changes.
			// NOTE: this is sent when the pinned state of a non-app tab changes and is
			// sent in addition to TabPinnedStateChanged. UI code typically need not care
			// about TabPinnedStateChanged, but instead this.
			virtual void TabMiniStateChanged(content::WebContents* contents, int index);

			// Invoked when the blocked state of a tab changes.
			// NOTE: This is invoked when a tab becomes blocked/unblocked by a tab modal
			// window.
			virtual void TabBlockedStateChanged(content::WebContents* contents, int index);

			// The TabStripModel now no longer has any tabs. The implementer may
			// use this as a trigger to try and close the window containing the
			// TabStripModel, for example...
			virtual void TabStripEmpty();

			// Sent when the tabstrip model is about to be deleted and any reference held
			// must be dropped.
			virtual void TabStripModelDeleted();

			// Invoked when an active/selected tab at |index| is selected again (ie - the
			// active/foreground tab is clicked).
			virtual void ActiveTabClicked(int index);

		protected:
			virtual ~TabStripModelObserver() {}
		};


		class ShellUnloadController : public content::NotificationObserver,
			public TabStripModelObserver {
		public:
			explicit ShellUnloadController(ShellWindow* browser);
			virtual ~ShellUnloadController();

			// Returns true if |contents| can be cleanly closed. When |browser_| is being
			// closed, this function will return false to indicate |contents| should not
			// be cleanly closed, since the fast shutdown path will just kill its
			// renderer.
			bool CanCloseContents(content::WebContents* contents);

			// Called when a BeforeUnload handler is fired for |contents|. |proceed|
			// indicates the user's response to the Y/N BeforeUnload handler dialog. If
			// this parameter is false, any pending attempt to close the whole browser
			// will be canceled. Returns true if Unload handlers should be fired. When the
			// |browser_| is being closed, Unload handlers for any particular content::WebContents
			// will not be run until every content::WebContents being closed has a chance to run
			// its BeforeUnloadHandler.
			bool BeforeUnloadFired(content::WebContents* contents, bool proceed);

			bool is_attempting_to_close_browser() const {
				return is_attempting_to_close_browser_;
			}

			// Called in response to a request to close |browser_|'s window. Returns true
			// when there are no remaining unload handlers to be run.
			bool ShouldCloseWindow();

			// Returns true if |browser_| has any tabs that have BeforeUnload handlers
			// that have not been fired. This method is non-const because it builds a list
			// of tabs that need their BeforeUnloadHandlers fired.
			// TODO(beng): This seems like it could be private but it is used by
			//             AreAllBrowsersCloseable() in application_lifetime.cc. It seems
			//             very similar to ShouldCloseWindow() and some consolidation
			//             could be pursued.
			bool TabsNeedBeforeUnloadFired();

		private:
			typedef std::set<content::WebContents*> UnloadListenerSet;

			// Overridden from content::NotificationObserver:
			virtual void Observe(int type,
				const content::NotificationSource& source,
				const content::NotificationDetails& details) OVERRIDE;

			// Overridden from TabStripModelObserver:
			virtual void TabInsertedAt(content::WebContents* contents,
				int index,
				bool foreground) OVERRIDE;
			virtual void TabDetachedAt(content::WebContents* contents, int index) OVERRIDE;
			virtual void TabReplacedAt(TabStripModel* tab_strip_model,
				content::WebContents* old_contents,
				content::WebContents* new_contents,
				int index) OVERRIDE;
			virtual void TabStripEmpty() OVERRIDE;

			void TabAttachedImpl(content::WebContents* contents);
			void TabDetachedImpl(content::WebContents* contents);

			// Processes the next tab that needs it's beforeunload/unload event fired.
			void ProcessPendingTabs();

			// Whether we've completed firing all the tabs' beforeunload/unload events.
			bool HasCompletedUnloadProcessing() const;

			// Clears all the state associated with processing tabs' beforeunload/unload
			// events since the user cancelled closing the window.
			void CancelWindowClose();

			// Removes |web_contents| from the passed |set|.
			// Returns whether the tab was in the set in the first place.
			bool RemoveFromSet(UnloadListenerSet* set,
				content::WebContents* web_contents);

			// Cleans up state appropriately when we are trying to close the browser and
			// the tab has finished firing its unload handler. We also use this in the
			// cases where a tab crashes or hangs even if the beforeunload/unload haven't
			// successfully fired. If |process_now| is true |ProcessPendingTabs| is
			// invoked immediately, otherwise it is invoked after a delay (PostTask).
			//
			// Typically you'll want to pass in true for |process_now|. Passing in true
			// may result in deleting |tab|. If you know that shouldn't happen (because of
			// the state of the stack), pass in false.
			void ClearUnloadState(content::WebContents* web_contents, bool process_now);

			ShellWindow* browser_;

			content::NotificationRegistrar registrar_;

			// Tracks tabs that need there beforeunload event fired before we can
			// close the browser. Only gets populated when we try to close the browser.
			UnloadListenerSet tabs_needing_before_unload_fired_;

			// Tracks tabs that need there unload event fired before we can
			// close the browser. Only gets populated when we try to close the browser.
			UnloadListenerSet tabs_needing_unload_fired_;

			// Whether we are processing the beforeunload and unload events of each tab
			// in preparation for closing the browser. ShellUnloadController owns this state
			// rather than Browser because unload handlers are the only reason that a
			// Browser window isn't just immediately closed.
			bool is_attempting_to_close_browser_;

			base::WeakPtrFactory<ShellUnloadController> weak_factory_;

			DISALLOW_COPY_AND_ASSIGN(ShellUnloadController);
		};

	} //namespace shell 
} //namespace cuc 
#endif //CUC_CWKSHELL_VIEW_SHELL_UNLOAD_CONTROLLER_H_ 
